/*******************************************************************************
 * Copyright (c) 2014 Steffen Mikkelsen
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     Steffen Mikkelsen - initial API and implementation
 ******************************************************************************/
package com.subsecret.secureconnection.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

import com.subsecret.secureconnection.securestream.SecureStream;
import com.subsecret.secureconnection.streamtools.ForwardDataThread;
import com.subsecret.secureconnection.streamtools.StreamConstants;
import com.subsecret.secureconnection.streamtools.StreamTools;

public class StreamForwardServer {
    private final int serverport;
    private final String password;
    
    public StreamForwardServer(int serverport, String password) {
        this.password = password;
        this.serverport = serverport;
    }
    
    public void start() throws IOException {
        @SuppressWarnings("resource")
        ServerSocket ssock = new ServerSocket(serverport);
        while (true) {
            try {
                Socket lsock = ssock.accept();
                new ConnectionHandler(lsock, password).start();
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static class ConnectionHandler extends Thread {
        private Socket socket;
        private String password;
        
        public ConnectionHandler(Socket sock, String password) {
            this.socket = sock;
            this.password = password;
        }
        
        @Override
        public void run() {
            try {
                
                InputStream serverIn = socket.getInputStream();
                OutputStream serverOut = socket.getOutputStream();
                
                SecureStream createSecureStreamServer = SecureStream.createSecureStreamServer(serverIn, serverOut, false);
                
                serverIn = createSecureStreamServer.getInputStream();
                serverOut = createSecureStreamServer.getOutputStream();
                String pass = StreamTools.receiveString(serverIn);
                
                if (!pass.equals(password)) {
                    StreamTools.sendByte(serverOut, StreamConstants.PASSWORD_FAIL);
                    socket.close();
                    return;
                }
                StreamTools.sendByte(serverOut, StreamConstants.PASSWORD_OK);
                
                String host = StreamTools.receiveString(serverIn);
                int port = StreamTools.receiveInteger(serverIn);
                Socket appSock = null;
                try {
                    appSock = new Socket(host, port);
                }
                catch (Exception e) {
                    StreamTools.sendByte(serverOut, StreamConstants.CONN_FAIL);
                    socket.close();
                    return;
                }
                StreamTools.sendByte(serverOut, StreamConstants.CONN_OK);
                
                // Multiplexing logic can be added here
                
                InputStream aIn = appSock.getInputStream();
                OutputStream aOut = appSock.getOutputStream();
                
                ForwardDataThread f1 = new ForwardDataThread(serverIn, aOut);
                ForwardDataThread f2 = new ForwardDataThread(aIn, serverOut);
                f1.start();
                f2.start();
                try {
                    f1.join();
                    f2.join();
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
                appSock.close();
                socket.close();
            }
            catch (IOException e) {
                e.printStackTrace();
            }
            
        }
    }
}
